package com.roadmap.biz.life.plan;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.roadmap.biz.common.BaseManager;
import com.roadmap.biz.common.RdcQueryConst;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcResourceBundle;
import com.roadmap.common.BaseEnum.ActionType;
import com.roadmap.entities.LifeGoal;
import com.roadmap.entities.LifePlan;
import com.roadmap.entities.LimitedAccess;
import com.roadmap.entities.ext.PlanExt;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NotAuthorizedException;
import com.roadmap.exception.NotMatchedException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.RdcQuery;

public class PlanManager extends BaseManager {

	public PlanManager(String currentUser, String itemOwner) {
		super(currentUser, itemOwner, LifePlan.class, LifeGoal.class);
	}

	@Override
	public void create(BaseEntity entity) throws NullObjectException,
			InvalidValueException, ServerException, InvalidActionException,
			EntityNotFoundException, ActionNotSupportedException,
			NotAuthorizedException {
		checkEntityIsValid(entity);

		LifePlan plan = (LifePlan) entity;
		if (plan.getAspirantId() == null) {
			plan.setAspirantId(getCurrentUserId());
		}
		else {
			checkUserCUD(plan.getAspirantId());
		}

		LifeGoal goal = (LifeGoal) getParant(plan.getGoalId());
		checkUserCUD(goal.getAspirantId());

		// 检查目标的状态，只能为unscheduled的目标创建计划
		if (goal.getStatus() != BaseConst.LIFEGOAL_STATUS_UNSCHEDULED) {
			ActionNotSupportedException e = new ActionNotSupportedException();
			_log.error("Only unscheduled goals allow new plans created", e);
			throw e;
		}

		createPlanAndUpdateGoal(plan, goal);
	}

	private void createPlanAndUpdateGoal(LifePlan plan, LifeGoal goal)
			throws NullObjectException, ServerException, InvalidActionException {
		Vector<BaseEntity> entities = new Vector<BaseEntity>();
		entities.add(plan);

		// 更新目标
		goal.setStatus(BaseConst.LIFEGOAL_STATUS_SCHEDULED);
		goal.setUpdatedTime(plan.getCreatedTime());
		entities.add(goal);

		if (plan.getVisibility() == BaseConst.ENTRY_VISIBILITY_LIMITED) {
			// 更新limited权限记录
			entities.add(new LimitedAccess(plan.getId(), plan
					.getLimitedMembers()));
		}

		getDbUtil().saveOrUpdateEntities(entities);
		getNoticeManager().notifyForEntry(plan, ActionType.CREATE);
	}

	/** 删除计划时，只更新目标和任务的状态值，但不删掉任何数据 */
	@Override
	public void delete(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, InvalidActionException,
			EntityNotFoundException, NotAuthorizedException {
		LifePlan plan = (LifePlan) get(entryId);
		if (plan == null) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error("Plan was not found by id " + entryId, e);
			throw e;
		}

		Session session = getDbUtil().getSession();
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			Date dt = new Date();
			// 更新相应的scheduled目标，
			Query q = session.createQuery(RdcQueryConst.SQL_UPDATE_GOAL_STATUS);
			q.setParameter("gid", plan.getGoalId());
			q.setParameter("state", BaseConst.LIFEGOAL_STATUS_UNSCHEDULED);
			q.setParameter("dt", dt);
			q.setParameter("state2", BaseConst.LIFEGOAL_STATUS_SCHEDULED);
			q.executeUpdate();

			// 更新计划下的任务
			q = session.createQuery(RdcQueryConst.SQL_UPDATE_TASKS_STATUS);
			q.setParameter("pid", plan.getId());
			q.setParameter("state", BaseConst.LIFETASK_STATUS_DELETED);
			q.setParameter("dt", dt);
			q.executeUpdate();
			
			// 删除计划
			q = session.createQuery(RdcQueryConst.HQL_DELETE_PLAN);
			q.setParameter("pid", plan.getId());
			q.executeUpdate();

			tx.commit();
		}
		catch (HibernateException e) {
			_log.error(e);
			if (tx != null) {
				tx.rollback();
			}

			throw e;
		}
		finally {
			session.close();
		}
	}

	@Override
	public PlanExt getEntityExt(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException,
			EntityNotFoundException, NotMatchedException,
			NotAuthorizedException, ActionNotSupportedException {
		PlanExt planExt = retrievePlanFromDB(entityId);

		// 计划的进度等信息
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_PLAN_STATISTICS);
		query.addScalar("amount", Hibernate.INTEGER);
		query.addScalar("assigned", Hibernate.INTEGER);
		query.addScalar("progress", Hibernate.INTEGER);
		query.setParameter("pid", entityId);
		query.setParameter("state", BaseConst.LIFETASK_STATUS_DELETED);
		Object[] result = (Object[]) query.getUniqueResult();

		// 计划可能没有任务
		if (result != null) {
			planExt.setTaskCount(((Integer) result[0]).intValue());

			// 计划可能没有执行记录
			if (result[1] != null) {
				planExt.setAssignedProportion(((Integer) result[1]).intValue());
			}
			if (result[2] != null) {
				planExt.setProgress(((Integer) result[2]).intValue() / 100);
			}
		}
		return planExt;
	}

	private PlanExt retrievePlanFromDB(String entityId) throws ServerException,
			EntityNotFoundException, InvalidQueryException,
			InvalidValueException, NotMatchedException, NotAuthorizedException,
			ActionNotSupportedException {
		if (entityId == null) {
			NullObjectException e = new NullObjectException();
			_log.error("Plan id is NULL.", e);
			throw e;
		}

		// 查询计划信息
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_PLAN_WITH_GOAL);
		query.addEntity("plan", LifePlan.class);
		query.addScalar("name");
		query.addScalar("status");
		query.setParameters(new Object[] { getOwnerId(), entityId,
				BaseConst.LIFEGOAL_STATUS_DELETED });
		Object[] result = (Object[]) query.getUniqueResult();
		if (result == null) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error(String.format("Plan was not found by id %s ", entityId),
					e);
			throw e;
		}

		LifePlan plan = (LifePlan) result[0];
		if (!checkUserAccess(plan)) {
			// 如果当前用户未授权访问此项，抛出异常
			NotAuthorizedException e = new NotAuthorizedException();
			_log.error(String.format("Access is not allowed for user %s",
					getCurrentUserId()), e);
			throw e;
		}

		PlanExt planExt = new PlanExt(plan);
		planExt.setGoalName((String) result[1]);
		planExt.setStatus(((Byte) result[2]).intValue());
		planExt.setSymptom(diagnose(planExt));
		return planExt;
	}

	@Override
	public List<PlanExt> getExecutableEntities()
			throws ActionNotSupportedException, InvalidQueryException,
			InvalidValueException, ServerException, NotMatchedException {
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_PLANS_WITH_GOALS);
		query.addEntity("plan", LifePlan.class);
		query.addScalar("name");
		query.addScalar("status");
		query.setParameters(new Object[] { getOwnerId(),
				BaseConst.LIFEGOAL_STATUS_SCHEDULED,
				BaseConst.LIFEGOAL_STATUS_UNDERGOING });
		List<Object[]> list = query.getResults();
		if (list == null || list.isEmpty()) {
			return null;
		}

		// 存放当前用户可访问的任务
		Map<String, PlanExt> planMap = new LinkedHashMap<String, PlanExt>();
		findAuthorizedPlans(list, planMap);
		computePlansProgress(planMap);
		return getResultsForSpecifiedPage(planMap.values());
	}

	private void findAuthorizedPlans(List<Object[]> results,
			Map<String, PlanExt> planMap) throws ActionNotSupportedException,
			InvalidValueException, ServerException, NotMatchedException {
		// 存放权限为limited的任务的ID
		List<String> limitedTaskIDs = new ArrayList<String>();
		
		Iterator<Object[]> iter = results.iterator();
		Date today = new Date();
		while (iter.hasNext()) {
			Object[] result = iter.next();
			LifePlan plan = (LifePlan) result[0];

			// 如果plan的权限是private，而当前用户不是其拥有者
			if (plan.getVisibility() == BaseConst.ENTRY_VISIBILITY_PRIVATE
					&& !getCurrentUserId().equals(getOwnerId())) {
				continue;
			}

			PlanExt ext = new PlanExt(plan);
			ext.setGoalName((String) result[1]);
			ext.setStatus((Byte) result[2]);
			ext.setSymptom(diagnose(ext, today));
			planMap.put(plan.getId(), ext);

			// 若任务权限是limited，需进一步确认用户权限
			if (plan.getVisibility() == BaseConst.ENTRY_VISIBILITY_LIMITED) {
				limitedTaskIDs.add(plan.getId());
			}
		}

		filterLimitedEntities(limitedTaskIDs.toArray(new String[0]), planMap);
	}

	private void computePlansProgress(Map<String, PlanExt> planMap)
			throws ServerException {
		if (planMap == null || planMap.isEmpty()) {
			return;
		}

		// 计算各计划的进度
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_PLANS_STATISTICS);
		query.addScalar("pid", Hibernate.STRING);
		query.addScalar("amount", Hibernate.INTEGER);
		query.addScalar("progress", Hibernate.INTEGER);
		query.setParameterList("planIDs", planMap.keySet().toArray());
		query.setParameter("state1", BaseConst.LIFETASK_STATUS_UNDERGOING);
		query.setParameter("state2", BaseConst.LIFETASK_STATUS_COMPLETED);
		List<Object[]> results = query.getResults();
		if (results == null || results.isEmpty()) {
			return;
		}

		Iterator<Object[]> iter = results.iterator();
		while (iter.hasNext()) {
			Object[] rs = (Object[]) iter.next();
			PlanExt ext = planMap.get(rs[0]);
			ext.setTaskCount(((Integer) rs[1]).intValue());
			if (rs[2] != null) {
				ext.setProgress(((Integer) rs[2]).intValue() / 100);
			}
		}
	}

	/** 诊断计划的健康状况 */
	private String diagnose(PlanExt plan) throws ActionNotSupportedException,
			InvalidValueException {
		return diagnose(plan, new Date());
	}

	private String diagnose(PlanExt plan, Date ref)
			throws ActionNotSupportedException, InvalidValueException {
		int status = plan.getStatus();
		if (isStatusFitForDiagnose(status)) {
			Date start = plan.getScheduledStartDate();
			Date end = plan.getScheduledEndDate();
			
			// 对比预定日期与当前时间
			if (status == BaseConst.LIFEGOAL_STATUS_SCHEDULED
					&& start.before(ref)) {
				return RdcResourceBundle.Entity_Symptom_Not_Started;
			}

			if (status == BaseConst.LIFEGOAL_STATUS_UNDERGOING
					&& end.before(ref)) {
				return RdcResourceBundle.Entity_Symptom_Not_Ended;
			}
		}

		return RdcResourceBundle.Entity_Symptom_Healthy;
	}

	private boolean isStatusFitForDiagnose(int status) {
		// 只有unscheduled/scheduled/undergoing的计划才需要检验
		return status == BaseConst.LIFEGOAL_STATUS_UNSCHEDULED
				|| status == BaseConst.LIFEGOAL_STATUS_SCHEDULED
				|| status == BaseConst.LIFEGOAL_STATUS_UNDERGOING;
	}

	@Override
	public List getEntitiesOfOwner() throws ServerException,
			NullObjectException, NotMatchedException, InvalidValueException {
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_PLANS_BY_USER);
		query.addEntity("p", LifePlan.class);
		query.setParameters(new Object[] { getOwnerId(),
				BaseConst.LIFEGOAL_STATUS_DELETED });
		List list = query.getResults();
		return getAuthorizedEntities(list);
	}
}
